/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2013 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include "splashcore.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppLinkDebugInfoFlag

CppLinkDebugInfoFlag::CppLinkDebugInfoFlag()
{
	
}

CppLinkDebugInfoFlag::~CppLinkDebugInfoFlag()
{
}

CppLinkFlag* CppLinkDebugInfoFlag::Clone() const
{
	return new CppLinkDebugInfoFlag;
}

string CppLinkDebugInfoFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-g";
	else
		FatalError("CppLinkDebugInfoFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppLinkerScriptFlag

CppLinkerScriptFlag::CppLinkerScriptFlag(string name)
	: m_name(name)
{
	
}

CppLinkerScriptFlag::~CppLinkerScriptFlag()
{
}

CppLinkFlag* CppLinkerScriptFlag::Clone() const
{
	return new CppLinkerScriptFlag(m_name);
}

string CppLinkerScriptFlag::toString(const CppToolchain* toolchain)
{
	//For g++, just link directly to the script (and specify it as a search directory)
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return string("-T ") + m_name + " " + "-L " + GetDirOfFile(m_name);
	else
		FatalError("CppLinkerScriptFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppBareMetalFlag

CppBareMetalFlag::CppBareMetalFlag()
{
	
}

CppBareMetalFlag::~CppBareMetalFlag()
{
}

CppLinkFlag* CppBareMetalFlag::Clone() const
{
	return new CppBareMetalFlag;
}

string CppBareMetalFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-nodefaultlibs -nostartfiles -nostdlib -fno-builtin -z max-page-size=2048 -z common-page-size=2048";
	else
		FatalError("CppBareMetalFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppDeadStripFlag

CppDeadStripFlag::CppDeadStripFlag()
{
	
}

CppDeadStripFlag::~CppDeadStripFlag()
{
}

CppLinkFlag* CppDeadStripFlag::Clone() const
{
	return new CppDeadStripFlag;
}

string CppDeadStripFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-Wl,--gc-sections";
	else
		FatalError("CppDeadStripFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppLinkLibraryByNameFlag

CppLinkLibraryByNameFlag::CppLinkLibraryByNameFlag(string name)
	: m_name(name)
{
	
}

CppLinkLibraryByNameFlag::~CppLinkLibraryByNameFlag()
{
}

CppLinkFlag* CppLinkLibraryByNameFlag::Clone() const
{
	return new CppLinkLibraryByNameFlag(m_name);
}

string CppLinkLibraryByNameFlag::toString(const CppToolchain* toolchain)
{
	//For g++, just link directly to the library
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return string("-l") + m_name;
	else
		FatalError("CppLinkLibraryByNameFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppLinkLibraryByPathFlag

CppLinkLibraryByPathFlag::CppLinkLibraryByPathFlag(string path)
	: m_path(CanonicalizePath(path))
{
	
}

CppLinkLibraryByPathFlag::~CppLinkLibraryByPathFlag()
{
}

CppLinkFlag* CppLinkLibraryByPathFlag::Clone() const
{
	return new CppLinkLibraryByPathFlag(m_path);
}

string CppLinkLibraryByPathFlag::toString(const CppToolchain* toolchain)
{
	//For g++, just link directly to the library
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return m_path;
	else
		FatalError("CppLinkLibraryByPathFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppLinkLibraryByTargetNameFlag

CppLinkLibraryByTargetNameFlag::CppLinkLibraryByTargetNameFlag(string name, BuildGraph* graph)
	: m_name(name)
	, m_graph(graph)
{
	
}

CppLinkLibraryByTargetNameFlag::~CppLinkLibraryByTargetNameFlag()
{
}

CppLinkFlag* CppLinkLibraryByTargetNameFlag::Clone() const
{
	return new CppLinkLibraryByTargetNameFlag(m_name, m_graph);
}

BuildGraphNode* CppLinkLibraryByTargetNameFlag::GetNode(const CppToolchain* toolchain)
{
	string arch = toolchain->GetArchitecture();
	if(m_graph->FindTargetForArch(m_name, arch) == m_graph->targetend())
		FatalError("Target \"%s\" for architecture \"%s\" does not exist, cannot link to it\n", m_name.c_str(), arch.c_str());
	return m_graph->GetTargetForArch(m_name, arch);
}

string CppLinkLibraryByTargetNameFlag::toString(const CppToolchain* toolchain)
{
	BuildGraphNode* node = GetNode(toolchain);
	
	//Get the actual path
	string path;
	CppSharedLibraryNode* shlibnode = dynamic_cast<CppSharedLibraryNode*>(node);
	CppStaticLibraryNode* stlibnode = dynamic_cast<CppStaticLibraryNode*>(node);
	if(shlibnode != NULL)
	{
		//for Windows, link to the filename
		if(toolchain->GetArchitecture().find("mingw") != string::npos)
			path = shlibnode->GetOutputPath();
		
		//for Linux, link to the libname
		else
			path = string("-l") + shlibnode->GetName();
	}
	else if(stlibnode != NULL)
		path = stlibnode->GetOutputPath();
	else
		FatalError("Target %s is not a shared or static library, cannot link to it\n", m_name.c_str());
		
	//For g++, just link directly to the library
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return path;
	else
		FatalError("CppLinkLibraryByTargetNameFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppLinkProfilingFlag

CppLinkProfilingFlag::CppLinkProfilingFlag()
{
	
}

CppLinkProfilingFlag::~CppLinkProfilingFlag()
{
}

CppLinkFlag* CppLinkProfilingFlag::Clone() const
{
	return new CppLinkProfilingFlag;
}

string CppLinkProfilingFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-fprofile-arcs";
	else
		FatalError("CppLinkProfilingFlag is not supported on this compiler yet\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CppLinkOpenMPFlag

CppLinkOpenMPFlag::CppLinkOpenMPFlag()
{
	
}

CppLinkOpenMPFlag::~CppLinkOpenMPFlag()
{
}

CppLinkFlag* CppLinkOpenMPFlag::Clone() const
{
	return new CppLinkOpenMPFlag;
}

string CppLinkOpenMPFlag::toString(const CppToolchain* toolchain)
{
	if(dynamic_cast<const GnuCppToolchain*>(toolchain) != NULL)
		return "-fopenmp";
	else
		FatalError("CppLinkOpenMPFlag is not supported on this compiler yet\n");
}
